package ast;

import java.util.ArrayList;
import java.util.Hashtable;

public class SymbolTable {
	public Hashtable<String, ArrayList<StoreSymbol>> table;
	public String name;
	public SymbolTable parent;
	private int bodyCount = 0;
	private int checkBodyCount = 0;
	public ArrayList<SymbolTable> children = new ArrayList<SymbolTable>();

	public SymbolTable() {
		// initialise table
		table = new Hashtable<String, ArrayList<StoreSymbol>>();
	}

	public void put(String key, StoreSymbol object) {
		if (!table.containsKey(key)) {
			ArrayList<StoreSymbol> tmp = new ArrayList<StoreSymbol>();
			tmp.add(object);
			table.put(key, tmp);
		} else {
			table.get(key).add(object);
			// ArrayList<StoreSymbol> value = table.get(key);
			// value.add(object);
			// table.put(key, value);
		}
	}
	
	/**
	 * Returns an ArrayList of StoreSymbol contained in the HashTable of the SymbolTable object.
	 * 
	 * @param key
	 * @return ArrayList<StoreSymbol>
	 */
	public ArrayList<StoreSymbol> get(String key) {
		if (table.containsKey(key)) {
			return table.get(key);	
		}
		else {
			return null;
		}
	}

	// FOR CHECKING DUPLICATE METHOD
	public ArrayList<MethodStoreSymbol> getMethods(String key) {
		ArrayList<MethodStoreSymbol> tmp = new ArrayList<MethodStoreSymbol>();
		if (table.containsKey(key)) {
			for (StoreSymbol symbol : table.get(key)) {
				try {
					MethodStoreSymbol tmpSymbol = (MethodStoreSymbol) symbol;
					tmp.add(tmpSymbol);
				} catch (ClassCastException e) {

				}
			}
		} else {
			return null;
		}
		return tmp;
	}

	public Boolean duplicatedInstVar(String key) {
		if (table.containsKey(key))
			return true;
		return false;
	}
	
	/**
	 * To begin a new scope. Creates a new SymbolTable object ant puts it in the ArrayList 
	 * of children of the current SymboTable. Returns the newly created SymbolTable.
	 * 
	 * @return SymbolTable
	 */
	public SymbolTable beginScope() {
		SymbolTable newTable = new SymbolTable();
		newTable.setparent(this);
		System.out.println("++++++++++++++ OPEN +++++++++++++++");
		children.add(newTable);
		return newTable;
	}

	private void setparent(SymbolTable newParent) {
		parent = newParent;
	}

	public void setName(String newName) {
		name = newName;
	}

	// FOR THE DUPLICATE METHOD CHECKING
	public String getName() {
		return name;
	}

	public void incBodyCount() {
		bodyCount++;
	}

	public int getBodyCount() {
		return bodyCount;
	}

	public SymbolTable endScope() {
		System.out.println("------------- CLOSED --------------");
		return parent;
	}

	public void printTable() {
		System.out.println("+");
		System.out.println("Scope name: " + name);
		for (String s : table.keySet()) {
			System.out.println(" " + s);
		}
		System.out.println("-");
	}
	
	/**
	 * Returns the child scope (SymbolTable object) of the current scope of a specified name.
	 * 
	 * @param id
	 * @return SymbolTable
	 */
	public SymbolTable enterScope(String id) {
		for (SymbolTable table : children) {
			if (table.name.equals(id)) {
				return table;
			}
		}
		return null;
	}
	
	/**
	 * Returns the SymbolTable set as parent of the current SymbolTable.
	 * 
	 * @return SymbolTable
	 */
	public SymbolTable exitScope() {
		return parent;
	}

	public void incCheckBodyCount() {
		checkBodyCount++;
	}

	public int getCheckBodyCount() {
		return checkBodyCount;
	}
	
	/**
	 * Returns a SymbolTable containing only the public fields found in the current SymbolTable.
	 * 
	 * @return SymbolTable
	 */
	public SymbolTable getPublicTable() {
		SymbolTable publicTable = new SymbolTable();
		for (String key : table.keySet()) {
			for (StoreSymbol symbol : table.get(key)) {
				if (symbol.getAccess().equals(new String("public"))) {
					publicTable.put(key, symbol);
				}
			}
		}
		return publicTable;
	}
	
	public SymbolTable getPrivateTable() {
		SymbolTable publicTable = new SymbolTable();
		for (String key : table.keySet()) {
			for (StoreSymbol symbol : table.get(key)) {
				if (symbol.getAccess().equals(new String("private"))) {
					publicTable.put(key, symbol);
				}
			}
		}
		return publicTable;
	}
}
